#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h> 
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define key_name "keyInt"              //key的设备名
#define key_num  1                     //key设备数量

/* 寄存器名 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO018;
static void __iomem *SW_PAD_GPIO1_IO018;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

/* 中断IO描述结构体 */
struct irq_keydesc {
	int irqnum;								/* 中断号     */
	unsigned char value;					/* 按键对应的键值 */
	char name[10];							/* 名字 */
	irqreturn_t (*handler)(int, void *);	/* 中断服务函数 */
};

struct key_dev_t
{
    char *name;                     //设备名
    int num;                    //设备数量
    dev_t key_devid;                //key设备号
    struct cdev key_cdev;	        //cdev结构体	    
	struct class *key_class;	    //key类
	struct device *key_device;	    //key设备
    struct file_operations *fops;     //操作函数
    struct irq_keydesc irqkeydesc;   //中断相关
};

static irqreturn_t key_handler(int irq, void *dev_id)
{
	// struct imx6uirq_dev *dev = (struct imx6uirq_dev *)dev_id;

	// dev->curkeynum = 0;
	// dev->timer.data = (volatile long)dev_id;
	// mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));	/* 10ms定时 */
    printk("Thr INT is Triged\r\n");
	return IRQ_RETVAL(IRQ_HANDLED);
}

static int key_open(struct inode *inode, struct file *filp)
{
	return 0;
}

static ssize_t key_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

static ssize_t key_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    // // u32 ret;
    // int value;
    // value = readl(GPIO1_DR);
    // printk("value : %d\r\n",(value>>18)&0x01);
    // // ret = copy_to_user(buf, &value, sizeof(value));
    // // return ret;
    return 0;
}

/* 设备操作函数 */
static struct file_operations key_fops = {
	.owner = THIS_MODULE,
	.open = key_open,
	.write = key_write,
    .read = key_read,
};

struct key_dev_t key_dev;

static int key_probe(struct platform_device *dev)
{
    /* 获取key设备节点的信息并设置key为输入*/
    int i = 0;
    int ret;
	int ressize[5];
	u32 val = 0;
	struct resource *ledsource[5];

	printk("key driver and device has matched!\r\n");
	printk("platform_device name:%s,num:%d\r\n",dev->name,dev->num_resources);
	for (i = 0; i < 5; i++) {
		ledsource[i] = platform_get_resource(dev, IORESOURCE_MEM, i); /* 依次MEM类型资源 */
		if (!ledsource[i]) {
			dev_err(&dev->dev, "No MEM resource for always on\n");
			return -ENXIO;
		}
		ressize[i] = resource_size(ledsource[i]);	
	}	
    
    IMX6U_CCM_CCGR1 = ioremap(ledsource[0]->start, ressize[0]);
	SW_MUX_GPIO1_IO018 = ioremap(ledsource[1]->start, ressize[1]);
  	SW_PAD_GPIO1_IO018 = ioremap(ledsource[2]->start, ressize[2]);
	GPIO1_DR = ioremap(ledsource[3]->start, ressize[3]);
	GPIO1_GDIR = ioremap(ledsource[4]->start, ressize[4]);

    val = readl(IMX6U_CCM_CCGR1);
    val &= ~(3<<26);
    val |=  3<<26;      
    writel(val, IMX6U_CCM_CCGR1); //使能GPIO_1时钟

    writel(5, SW_MUX_GPIO1_IO018); //复用为GPIO
	writel(0xF080, SW_PAD_GPIO1_IO018);//待定  设置电气特性

    val = readl(GPIO1_GDIR);        
	val &= ~(1 << 18);			
	val &= 0 << 18;			    //设置GPIO方向为输入
	writel(val, GPIO1_GDIR);
    /* 获取key设备节点的信息并设置key为输入*/

    /*获取中断号并申请中断*/
    memset(key_dev.irqkeydesc.name, 0, sizeof(key_dev.irqkeydesc.name));	/* 缓冲区清零 */
	memcpy(key_dev.irqkeydesc.name,"KEY_INT",sizeof(key_dev.irqkeydesc.name));
    key_dev.irqkeydesc.handler = key_handler;
    key_dev.irqkeydesc.irqnum = platform_get_irq(dev,0);
    printk("irqnum:%d\r\n",key_dev.irqkeydesc.irqnum);
    ret = request_irq(key_dev.irqkeydesc.irqnum, key_dev.irqkeydesc.handler, 
		                 IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, key_dev.irqkeydesc.name,NULL);
    if(ret < 0)
    {
			printk("irq request failed!\r\n");
			return -EFAULT;
	}
	/*获取中断号并申请中断*/
	
    /*申请设备号，注册字符设备*/
    key_dev.name = key_name;
    key_dev.num = key_num;
    alloc_chrdev_region(&key_dev.key_devid,0,key_dev.num,key_dev.name);//申请设备号
    key_dev.fops = &key_fops;
    key_dev.key_cdev.owner = THIS_MODULE;
    cdev_init(&key_dev.key_cdev,key_dev.fops);                         //绑定设备和操作函数
    cdev_add(&key_dev.key_cdev,key_dev.key_devid,key_dev.num);         //绑定设备和设备号，注册设备
    key_dev.key_class = class_create(THIS_MODULE,key_dev.name);        //创建类
    key_dev.key_device = device_create(key_dev.key_class, NULL, key_dev.key_devid, NULL, key_dev.name);//创建设备
    /*申请设备号，注册字符设备*/

    return 0;
}
static int key_remove(struct platform_device *dev)
{
	// iounmap(IMX6U_CCM_CCGR1);
	// iounmap(SW_MUX_GPIO1_IO03);
	// iounmap(SW_PAD_GPIO1_IO03);
	// iounmap(GPIO1_DR);
	// iounmap(GPIO1_GDIR);

	// cdev_del(&leddev.cdev);/*  删除cdev */
	// unregister_chrdev_region(leddev.devid, LEDDEV_CNT); /* 注销设备�? */
	// device_destroy(leddev.class, leddev.devid);
	// class_destroy(leddev.class);
	return 0;
}
static const struct of_device_id key_of_match[] = {
	{ .compatible = "MyKeyPlatformWithDTB" },
	{ /* 留空 */ }
};

/* platform驱动结构�? */
static struct platform_driver platform_key_driver = {
	.driver		= {
		.name	= "MyPlatformKey",			/* 驱动名字，用于和设备匹配 */
		.of_match_table	= key_of_match,     /* 设备树匹配表，用于和设备匹配*/
	},
	.probe		= key_probe,
	.remove		= key_remove,
};

static int __init keyDrver_Init(void)
{
	printk("Begin Init...\r\n");
    platform_driver_register(&platform_key_driver);
    return 0;
}

static void __exit keyDrver_Exit(void)
{

}

module_init(keyDrver_Init);
module_exit(keyDrver_Exit);
MODULE_AUTHOR("ZiXie");
MODULE_LICENSE("GPL");